<?php
/**
 * 法大大接口服务类
 * @author River
 */

namespace App\Service\Fdd;

class FddClient
{
    private string $appId = '';
    private string $appSecret = '';
    private int $timeout = 60;

    /**
     * 接口请求域名
     *
     * 生产环境域名：https://api.fadada.com/api/v5/
     * 开发环境域名：https://uat-api.fadada.com/api/v5/
     *
     */
    private string $baseUrl = 'https://uat-api.fadada.com/api/v5/';
    private string $version = '';

    public function __construct($appId, $appSecret, $version = '5.1', $timeout = 60)
    {
        $this->appId = $appId;
        $this->appSecret = $appSecret;
        $this->version = $version;
        $this->timeout = $timeout;
        if (env('FDD_ENV') == 'production') {
            $this->baseUrl = 'https://api.fadada.com/api/v5/';
        }
    }

    public function request($accessToken, $bizContent, $path){
        //随机数
        $nonce = md5(time().mt_rand(0,1000));
        $headers = $this->getHeader($nonce, $bizContent, $accessToken);
        $headers['Content-type'] = "application/x-www-form-urlencoded";
        $postHeader = $this->toPost($headers);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->baseUrl . $path);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $postHeader);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        if(!is_null($bizContent)){
            $body = array();
            $body['bizContent'] = $bizContent;
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($body));
        }
        $response = curl_exec($ch);
        curl_close($ch);
        $result = json_decode($response, true);
        if ($result['code'] != 100000) {
            throw new \Exception('fdd request error : ' . $result['msg']);
        }
        return $result['data'];
    }

    public function toPost(array $params = array(), $pre = ''): array
    {
        $result = array();
        foreach($params as $k => $v){
            $result[] = $k.": ".$v;
        }
        return $result;
    }

    private function getHeader($nonce, $bizContent, $accessToken){
        //组装需要去计算签名的参数
        $date = $this->getTimestamp();
        $values = array();
        $values['X-FASC-App-Id'] = $this->appId;
        $values['X-FASC-Timestamp'] = $date;
        $values['X-FASC-Sign-Type'] = "HMAC-SHA256";
        $values['X-FASC-Nonce'] = $nonce;
        $values['X-FASC-Api-SubVersion'] = $this->version;
        if(is_null($accessToken)){
            $values['X-FASC-Grant-Type'] = "client_credential";
        }else{
            $values['X-FASC-AccessToken'] = $accessToken;
            $values['bizContent'] = $bizContent;
        }
        //得到排序后的字符串
        $keys = array_keys($values);
        array_multisort($keys, SORT_ASC, SORT_STRING);
        $sortParam = $this->arrayParamToStr($values, $keys);
        //计算签名
        $signText = strtolower(hash("sha256", $sortParam));
        $hash = hash_hmac("sha256", $date, $this->appSecret, true);
        $sign = hash_hmac("sha256", $signText, $hash);

        //返回header数组
        if(!is_null($accessToken)){
            $values = $this->removeKey($values);
        }
        $values['X-FASC-Sign'] = $sign;
        return $values;
    }

    private function arrayParamToStr($array, $keys): string
    {
        $Str = "";
        foreach($keys as $k => $v){
            $Str .= $v."=".$array[$v]."&";
        }
        return trim($Str,"&");
    }


    private function removeKey($data){
        if(!array_key_exists("bizContent", $data)){
            return $data;
        }
        $keys = array_keys($data);
        $index = array_search("bizContent", $keys);
        if($index !== FALSE){
            array_splice($data, $index, 1);
        }
        return $data;
    }

    private function getTimestamp(): float
    {
        list($mt, $sec) = explode(' ', microtime());
        return (float)sprintf('%.0f', (floatval($mt) + floatval($sec)) * 1000);
    }
}
